package com.io.file;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.io.file.FileMode;
import cn.hutool.core.lang.Console;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

/**
 * @author xin.ding
 * @date 2021/7/16 9:03 上午
 */
public class BigFileRead {

    public void read() {

    }

    /**
     * 以字节为单位读取文件，常用于读二进制文件，如图片、声音、影像等文件。
     */
    public static void readFileByBytes(String fileName) {

        File file = new File(fileName);
        InputStream in;
        try {
            System.out.println("以字节为单位读取文件内容，一次读一个字节：");
//            // 一次读一个字节
            in = new FileInputStream(file);
            showAvailableBytes(in);

            int tempbyte;
            int count = 0;
            while ((tempbyte = in.read()) != -1) {
                System.out.write(tempbyte);
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        try {
            System.out.println("以字节为单位读取文件内容，一次读多个字节：");
            // 一次读多个字节
            byte[] tempbytes = new byte[100];
            int byteread = 0;
            in = new FileInputStream(fileName);
            showAvailableBytes(in);
            // 读入多个字节到字节数组中，byteread为一次读入的字节数

            while ((byteread = in.read(tempbytes)) != -1) {
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    /**
     * 显示输入流中还剩的字节数
     */
    private static void showAvailableBytes(InputStream in) {
        try {
            System.out.println("当前字节输入流中的字节数为:" + in.available());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private static void readLines(String path) {
        TimeInterval timer = DateUtil.timer();
        List<String> strings = FileUtil.readLines(path, Charset.defaultCharset());
        Console.log("数据量:{} 耗时:{}ms", strings.size(), timer.intervalMs());
    }

    private static void readBytes(String path) {
        TimeInterval timer = DateUtil.timer();
        byte[] bytes = FileUtil.readBytes(path);
        Console.log("数据量:{} 耗时:{}ms", bytes.length, timer.intervalMs());
    }

    /**
     * 优化随机访问
     *
     * @param path
     */
    private static void readBufferedRandomAccessFile(String path) {
        TimeInterval timer = DateUtil.timer();
        RandomAccessFile randomAccessFile = null;
        try {
            randomAccessFile = new BufferedRandomAccessFile(path, FileMode.r.name());
        } catch (IOException e) {
            e.printStackTrace();
        }
        List<String> ret = new ArrayList<>();
        FileUtil.readLines(randomAccessFile, Charset.defaultCharset(), line -> ret.add(line));

        Console.log("数据量:{} 耗时:{}ms", ret.size(), timer.intervalMs());
        System.out.println(ret.size());
    }

    private static void readRandom(String path) {
        TimeInterval timer = DateUtil.timer();
        File file = FileUtil.file(path);
        RandomAccessFile randomAccessFile = FileUtil.createRandomAccessFile(file, FileMode.r);
        FileUtil.readLines(randomAccessFile, Charset.defaultCharset(), new LineHandler() {
            @Override
            public void handle(String line) {

            }
        });

        Console.log("数据量: 耗时:{}ms", timer.intervalMs());
    }


    public static void readWrite(String path) {
        try {
            RandomAccessFile rafi = new RandomAccessFile(path, "r");
            RandomAccessFile rafo = new RandomAccessFile(path + "_bck", "rw");
            FileChannel fci = rafi.getChannel();
            FileChannel fco = rafo.getChannel();
            long size = fci.size();
            MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_ONLY, 0, size);
            MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, size);
            long start = System.currentTimeMillis();
            for (int i = 0; i < size; i++) {
                byte b = mbbi.get(i);
                mbbo.put(i, b);
            }
            fci.close();
            fco.close();
            rafi.close();
            rafo.close();
            System.out.println("Spend: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String path = "/Users/dx/test/krisk-data/market/2021-07-14.csv";
        readLines(path);
        readBytes(path);
        readBufferedRandomAccessFile(path);
        readRandom(path);
        readWrite(path);
    }

}

